#include "scene_manager.h"

#include "../share/log.h"
#include "../share/retcodes.h"
#include "../share/string_common.h"
#include "SceneConfigureTable.h"
#include "mapobj_manager.h"
#include "../share/time_module.h"
#include "../share/proxyclient_service.h"

CSceneManager::CSceneManager( )
{
	instance_id_ = (CUR_SEC) << 32;
}

CSceneManager::~CSceneManager( )
{
}

bool CSceneManager::Initialize( )
{
	Json::Value _jv = ReadJsonFile("data/map/scencelist.json");
	if (_jv == Json::nullValue)
	{
		LOG_ERROR("default", "ReadJsonFile -> scenelist.json failed ......");
		return false;
	}

	Json::Value::Members _tmp = _jv.getMemberNames();
	for (int32_t i = 0; i < _tmp.size(); ++i)
	{
		CSceneConfig scene_cfg;
		scene_cfg.scene_id_ = _jv[_tmp[i]]["scene_id"].asInt();
		scene_cfg.base_line_ = _jv[_tmp[i]]["base_line"].asInt();
		scene_cfg.max_line_ = _jv[_tmp[i]]["max_line"].asInt();
		scene_cfg.threshold_ = _jv[_tmp[i]]["threshold"].asInt();
		scene_cfg.max_player_count_ = _jv[_tmp[i]]["max_player_count"].asInt();
		
		sceneconfig_.scene_list_.push_back(scene_cfg);
	}

	GateSceneBegin();

	//int64_t instance_id = GenerateInstanceId();
	//CSceneInstance* instance = CreateSceneInstance(instance_id, 20000, 1);
	//if ( instance == NULL )
	//{
	//	LOG_ERROR("default", "create instance err!");
	//	return false;
	//}
	//instance_id = GenerateInstanceId();
	//instance = CreateSceneInstance(instance_id, 20100, 1);
	//if (instance == NULL)
	//{
	//	LOG_ERROR("default", "create instance err!");
	//	return false;
	//}

	return CreateInstanceByTpl();
}

bool CSceneManager::Finalize( )
{
	for( TScenes::iterator iter = scenes_.begin( ); iter != scenes_.end( ); ++iter )
	{
		OBJ_ID_TYPE scene_id = iter->second;
		CMAPObjManager::GetInstance( ).DestoryObject( scene_id );
	}
	scenes_.clear( );
	return true;
}

int64_t CSceneManager::GenerateInstanceId()
{
	return ++instance_id_;
}


void CSceneManager::GateSceneBegin()
{
	TMainSceneMap& main_scenes = GetInstance().main_scene_map_;
	for (int32_t i = 0; i < sceneconfig_.scene_list_.size(); ++i)
	{
		CSceneConfig& main_scene_config = sceneconfig_.scene_list_[i];
		CMainScene main_scene;
		main_scene.set_config(main_scene_config);
		main_scene.set_scene_id(main_scene_config.scene_id());
		for (int32_t i = 0; i < main_scene_config.base_line(); ++i)
		{
			main_scene.CreateLine();
		}
		main_scenes.insert(std::make_pair(main_scene_config.scene_id(), main_scene));

		LOG_INFO("default", "create main scene %d main scene num %d", main_scene_config.scene_id(), sceneconfig_.scene_list_.size());
	}
}

CSceneInstance* CSceneManager::CreateSceneInstance( int64_t instance_id, int32_t tpl_id, int32_t line_id)
{
	if ( scenes_.find( instance_id ) != scenes_.end( ) )
	{
		LOG_ERROR( "default", "scene is already exits" );
		return NULL;
	}
	SceneConfigureInfo* tpl = SceneConfigureTable::GetInstance().GetScenceConfigureInfoById(tpl_id);
	if ( NULL == tpl )
	{
		LOG_ERROR( "default", "tpl [%u] is not exits", tpl_id );
		return NULL;
	}
	CSceneInstance* ins = CMAPObjManager::GetInstance( ).CreateObject<CSceneInstance>( OBJTYPE_SCENE );
	if ( NULL == ins )
	{
		LOG_ERROR( "default", "create scene instance failed" );
		return NULL;
	}
	ins->set_instance_id( instance_id );
	ins->set_line_id( line_id );
	ins->set_main_scene_id(tpl_id);
	if ( ins->Setup( *tpl ) == false )
	{
		LOG_ERROR( "default", "setup tpl failed" );
		CMAPObjManager::GetInstance( ).DestoryObject( ins->get_id( ) );
		return NULL;
	}
	LOG_INFO( "default", "create scene[%u:%u:%ld]", ins->tpl_id( ), ins->get_id( ), instance_id );
	scenes_.insert( std::make_pair( instance_id, ins->get_id( ) ) );
	ins->OnSceneCreate( );

	TMainSceneMap::iterator it = main_scene_map_.find(tpl_id);
	if ( it != main_scene_map_.end() )
	{
		CLine* line = it->second.GetLine(line_id);
		if (line != NULL)
		{
			line->set_instance_id(instance_id);
		}
	}
	return ins;
}

bool CSceneManager::DestorySceneInstance( int64_t ins_id )
{
	TScenes::iterator iter = scenes_.find( ins_id );
	if ( iter == scenes_.end( ) )
	{
		return NULL;
	}
	OBJ_ID_TYPE obj_id = iter->second;
	CSceneInstance* ins = CMAPObjManager::GetInstance( ).GetObjectByID<CSceneInstance>( obj_id );
	if ( NULL == ins )
	{
		LOG_WARN( "default", "ins[%u] is not null", ins_id );
		return false;
	}
	LOG_INFO( "default", "destory scene instance[%u:%u:%ld]", ins->tpl_id( ), ins->get_id( ), ins_id );
	ins->OnSceneDestory( );
	CMAPObjManager::GetInstance( ).DestoryObject( obj_id );
	scenes_.erase( iter );
	return true;
}


CSceneInstance* CSceneManager::GetSceneInstance( int64_t instance_id )
{
	TScenes::iterator iter = scenes_.find( instance_id );
	if ( iter == scenes_.end( ) )
	{
		return NULL;
	}
	return CMAPObjManager::GetInstance( ).GetObjectByID<CSceneInstance>( iter->second );
}

void CSceneManager::TickUpdate( )
{
	//PERF_BEGIN( SceneMgrTick );
	for( TScenes::iterator iter = scenes_.begin( ); iter != scenes_.end( ); ++iter )
	{
		OBJ_ID_TYPE scene_id = iter->second;
		CSceneInstance* ins = CMAPObjManager::GetInstance( ).GetObjectByID< CSceneInstance >( scene_id );
		if( NULL == ins )
		{
			continue;
		}
		ins->TickUpdate( );
	}

	int32_t cur_tick = CUR_FRAME;
	if ((last_player_tick_ + 20) >= cur_tick)
	{
		CMAPObjManager::GetInstance().ObjManagerLoop(OBJTYPE_PLAYER, CSceneManager::PlayerUpdate, NULL);
		last_player_tick_ = cur_tick;
		return;
	}

	//PERF_END( SceneMgrTick );
}

bool CSceneManager::TeleportToSpawnPosition( Player& player )
{
	/*CSceneInstance* ins = GetSceneInstance( player.instance_id( ) );
	if( NULL == ins )
	{
		return false;
	}
	const CSceneTpl* tpl = CTPLManager::GetInstance( ).GetSceneTpl( ins->tpl_id( ) );
	if( NULL == tpl )
	{
		return false;
	}
	const CSpawnArea& spawn_area = tpl->spawn_area( );
	::common::CVector3 position;
	float angle = 0;
	if( spawn_area.GetRandomPosition( position, angle ) == false )
	{
		return ::common::ESCENE_REVIVE_NO_SPAWN_AREA;
	}
    CSceneScript& script = GetSceneScript( ins->script( ) );
    script.FixSpawnPosition( *ins, player, position, angle );
	CSceneManager::GetInstance( ).Teleport( player, player.instance_id( ), position, angle );*/
	return true;
}

void CSceneManager::OnPlayerChangeScene(Player& player, int32_t scene_id, int32_t line_id)
{
	CSceneInstance* cur_ins = CSceneManager::GetInstance( ).GetSceneInstance( player.instance_id( ) );
	if( NULL == cur_ins )
	{
		LOG_ERROR( "default", "cur ins is null" );
		return;
	}
// 	const CSceneTpl* cur_scene_tpl = CTPLManager::GetInstance( ).GetSceneTpl( cur_ins->tpl_id( ) );
// 	if( cur_scene_tpl == NULL )
// 	{
// 		LOG_ERROR( "default", "cur scene tpl is null" );
// 		return;
// 	}
// 	if( cur_scene_tpl->IsMainScene( ) != true )
// 	{
// 		CSceneLogic::GetInstance( ).MsgCodeNotify( player, ::common::ESCENE_CUR_SCENE_CANNOT_TELEPORT );
// 		return;
// 	}
// 	const CSceneTpl* scene_tpl = CTPLManager::GetInstance( ).GetSceneTpl( scene_id );
// 	if( NULL == scene_tpl )
// 	{
// 		LOG_ERROR( "default", "scene tpl is null %d", scene_id );
// 		return;
// 	}
// 	if( scene_tpl->IsMainScene( ) != true )
// 	{
// 		LOG_ERROR( "default", "scene %d is not main scene", scene_id );
// 		return;
// 	}
// 	int32_t ret_code = CheckEnter( player, scene_id );
// 	if ( ret_code == ::common::WAITING )
// 	{
// 		return;
// 	}
// 	if( ::common::SUCCESS != ret_code )
// 	{
// 		CSceneLogic::GetInstance( ).MsgCodeNotify( player, ret_code );
// 		return;
// 	}
// 
// 	PBMsgS2GChangeSceneRequest request;
// 	request.set_role_id( player.GetRoleID( ) );
// 	request.set_scene_id( scene_id );
// 	request.set_line_id( line_id );
// 
// 	send_2_gate( ID_S2G_CHANGE_SCENE_REQUEST, request, player.GetGateWayID( ) );
}

void CSceneManager::Dump( )
{
	LOG_INFO( "stat", "    %-18s  %-10s  %-10s  %-10s  %-10s", "tpl", "line", "instance", "player", "creature" );

	TScenes::iterator it = scenes_.begin( );
	for( ; it != scenes_.end( ); ++it )
	{
		OBJ_ID_TYPE obj_id = it->second;

		CSceneInstance* ins = CMAPObjManager::GetInstance( ).GetObjectByID<CSceneInstance>( obj_id );
		if( ins == NULL ) continue;

		ins->Dump( );
	}	
}

void CSceneManager::OnPlayerTeleport(Player& player, int32_t target_scene_id, const ::common::CVector3& target_position, float angle, int32_t line_id/* = 0*/)
{
	CSceneInstance* ins = CSceneManager::GetInstance( ).GetSceneInstance( player.instance_id( ) );
	if( NULL == ins )
	{
		LOG_ERROR( "default", "cur ins is null %d", player.GetPlayerId( ) );
		return;
	}

	if( ins->tpl_id( ) == target_scene_id  && ( line_id == 0 || ins->line_id() == line_id))
	{
	//	CSceneManager::GetInstance( ).Teleport( player, player.instance_id( ), target_position, angle );
	}
	else
	{
// 		int32_t ret_code = GetInstance().CheckEnter( player, target_scene_id );
// 		if ( ret_code == ::common::WAITING )
// 		{
// 			return;
// 		}
// 		if( ::common::SUCCEED != ret_code )
// 		{
// 			//CSceneLogic::GetInstance( ).MsgCodeNotify( player, ret_code );
// 			return;
// 		}
		//发送传送请求;
// 		PBMsgS2GChangeSceneRequest request;
// 		request.set_role_id( player.GetRoleID( ) );
// 		request.set_scene_id( target_scene_id );
// 		PBPosition* pb_position = request.mutable_position( );
// 		if( NULL != pb_position )
// 		{
// 			target_position.WriteToPB( *pb_position );
// 		}
// 		request.set_angle( angle );
// 		if ( line_id != 0 )
// 		{
// 			request.set_line_id(line_id);
// 		}
// 		send_2_gate( ID_S2G_CHANGE_SCENE_REQUEST, request, player.GetGateWayID( ) );
	}
}

CMainScene* CSceneManager::GetMainScene(int32_t scene_id)
{
	TMainSceneMap::iterator it = main_scene_map_.find(scene_id);
	if ( it == main_scene_map_.end() )
	{
		return NULL;
	}
	return &(it->second);
}

CMainScene* CSceneManager::GetMainSceneByinstance(int64_t instance_id)
{
	CSceneInstance* ins=CSceneManager::GetInstance().GetSceneInstance(instance_id);
	if (NULL == ins) {
		return NULL;
	}
	int32_t scene_id = ins->main_scene_id();

	TMainSceneMap::iterator it = main_scene_map_.find(scene_id);

	if (it == main_scene_map_.end()) {
		return NULL;
	}

	return &it->second;
}

CSceneInstance* CSceneManager::ChooseScene(int32_t scene_id, int32_t line_id, ::common::CVector3& position, int32_t& error_code)
{
	SceneConfigureInfo* tpl = SceneConfigureTable::GetInstance().GetScenceConfigureInfoById(scene_id);
	if ( tpl == NULL )
	{
		LOG_ERROR("default", "scene tpl is null [%d]", scene_id);
		error_code = -1;
		return NULL;
	}
	CMainScene* main_scene = GetMainScene(scene_id);
	if (NULL == main_scene)
	{
		error_code = -1;
		return NULL;
	}
	CLine* line = main_scene->ChooseLine(line_id);
	if (NULL == line)
	{
		error_code = -1;
		return NULL;
	}
	int32_t rtn = main_scene->LineCheckSerice(*line);
	if (rtn != common::SUCCEED) return NULL;
// 	if (position.zero() == true)
// 	{
// 		tpl->spawn_area().GetRandomPosition(position, angle);
// 	}
	if (scene_id == 20100)
	{
		position.set_x(35.92);
		position.set_y(0.12);
		position.set_z(39.71);
	}
	else if ( scene_id < 20100)
	{
		position.set_x(35.92);
		position.set_y(0.12);
		position.set_z(39.71);
	}
	return GetInstance().GetSceneInstance(line->instance_id());
}

bool CSceneManager::PlayerUpdate(::common::CObj& obj, void* param)
{
	CEntity& entity = (CEntity&)(obj);
	entity.TickUpdate();
	return true;
}

bool CSceneManager::ChooseSceneForClient(Player& player, const Json::Value& m_jv)
{
	uint32_t _to = m_jv["to"].asUInt();

	if (_to < 30000) {
		return false;
	}
	CSceneInstance* old_ins = CSceneManager::GetInstance().GetSceneInstance(player.instance_id());
	if (NULL != old_ins)
	{
		old_ins->PlayerLeave(player);
	}

	Json::Value target;
	target["msgid"] = M2G_SCENE_FOR_CLIENT;
	target["playerId"] = player.GetPlayerId();
	target["fr"] = m_jv["fr"].asUInt();
	target["to"] = _to;
	target["fl"] = m_jv["fl"].asUInt();
	std::string game_msg = JsonWriter(target);
	send_2_game(M2G_SCENE_FOR_CLIENT, game_msg, player.GetArea());
	return true;
}

bool CSceneManager::CheckSceneChange(Player& player, uint32_t scene_id)
{
	if (scene_id == (uint32_t)SCENE_MAP_ID::MAIN_CITY) {
		return true;
	}

	// 当前英雄战斗属性失败  
	// 第二个条件可以随时回 主城	
	if (!player.GetFightPro().CheckValid()) {
		//player.SyncHeroGame2Map(0);
		player.Send2CEnterScene(scene_id, ::common::ERR_LOGIN_PLAYER_DATA_ERR);
		return false;
	}
	return true;
}

bool CSceneManager::CreateInstanceByTpl()
{

	for (uint32_t i = 0; i < sceneconfig_.scene_list_.size(); ++i) {
		uint32_t scene_id = sceneconfig_.scene_list_[i].scene_id();

		if (scene_id >= (uint32_t)SCENE_MAP_ID::CLIENT_SCENE_ID_BEGIN) {
			LOG_ERROR("default", "create CSceneInstance id is bigger than CLIENT_SCENE_ID_BEGIN[%d] ", (uint32_t)SCENE_MAP_ID::CLIENT_SCENE_ID_BEGIN);
			return false;
		}
		int64_t instance_id = GenerateInstanceId();

		CSceneInstance* instance = CreateSceneInstance(instance_id, scene_id, 1);
		if (instance == NULL)
		{
			LOG_ERROR("default", "create instance err!");
			return false;
		}
	}

	return true;
}

CSceneInstance* CSceneManager::AutoChooseScene(int32_t scene_id, ::common::CVector3& dst_pos)
{
	int32_t error_code = 0;
	CMainScene* main_scence_cur = GetMainScene(scene_id);
	if (NULL == main_scence_cur) {
		LOG_ERROR("default", "scene_id[%d] no CMainScene", scene_id);
		return NULL;
	}

	SceneConfigureInfo* tpl = SceneConfigureTable::GetInstance().GetScenceConfigureInfoById(scene_id);
	if (tpl == NULL)
	{
		LOG_ERROR("default", "scene tpl is null [%d]", scene_id);
		error_code = -1;
		return NULL;
	}

	CLine* line = main_scence_cur->ChooseLine(1);

	if (line == NULL) {
		error_code = -1;
		return NULL;
	}


	if (line->instance_id() == 0) {
		uint32_t instance_id = GenerateInstanceId();
		CSceneInstance* scene_instance = CreateSceneInstance(instance_id, scene_id, line->line_id());
		if (scene_instance != nullptr) {
			return scene_instance;
		}
	}
	return ChooseScene(scene_id, line->line_id(), dst_pos, error_code);
}

//----------------------------------------------------
// 地图场景
//----------------------------------------------------
void CMainScene::set_config(const CSceneConfig& cfg)
{
	scene_config_.scene_id_ = cfg.scene_id_;
	scene_config_.base_line_ = cfg.base_line_;
	scene_config_.max_line_ = cfg.max_line_;
	scene_config_.threshold_ = cfg.threshold_;
	scene_config_.max_player_count_ = cfg.max_player_count_;
}

CLine* CMainScene::CreateLine()
{
	int32_t line_id = GenerateLineId();

	CLine line;
	line.set_line_id(line_id);
	line.set_instance_id(0);
	line.set_player_count(0);
	line.set_service_id(0);

	lines_.insert(std::make_pair(line_id, line));
	return GetLine(line_id);
}

CLine* CMainScene::GetLine(int32_t line_id)
{
	if (line_id == -1) return NULL;
	TLineMap::iterator iter = lines_.find(line_id);
	if (iter == lines_.end())
	{
		return NULL;
	}

	CLine& line = iter->second;

	int32_t rtn = LineCheckSerice(line);
	if (rtn == common::SUCCEED)
	{
		return &line;
	}

	return NULL;
}

CLine* CMainScene::ChooseLine(int32_t suggest_line)
{
	int32_t player_limit = (suggest_line == -1) ? scene_config_.threshold() * 0.7 : scene_config_.threshold();
	if (player_limit == 0)
	{
		if (scene_config_.threshold() > 0) player_limit = 1;
	}

	CLine* choose_line = GetLine(suggest_line);
	if (NULL != choose_line &&
		choose_line->player_count() < player_limit)
	{
		return choose_line;
	}
	int32_t player_count = 0;
	choose_line = NULL;
	for (TLineMap::iterator iter = lines_.begin(); iter != lines_.end(); ++iter)
	{
		CLine& line = iter->second;
		int32_t line_player_count = line.player_count();
		if (line.player_count() >= player_count &&
			line_player_count < player_limit)
		{
			player_count = line_player_count;
			choose_line = &line;
			break;
		}
	}
	if (choose_line == NULL)
	{
		for (TLineMap::iterator iter = lines_.begin(); iter != lines_.end(); ++iter)
		{
			CLine& line = iter->second;
			int32_t line_player_count = line.player_count();
			if (line.player_count() >= player_count &&
				line_player_count < player_limit)
			{
				player_count = line_player_count;
				choose_line = &line;
				break;
			}
		}
	}
	if (choose_line != NULL)
	{
		LOG_INFO("default", "choose line %d player count %d ", choose_line->line_id(), choose_line->player_count());
		return choose_line;
	}
	else
	{
		if ((int32_t)lines_.size() < scene_config_.max_line())
		{
			return CreateLine();
		}
		LOG_INFO("default", "choose line faild");
		return NULL;
	}
}


int32_t CMainScene::LineCheckSerice(CLine& line)
{
	return common::SUCCEED;
}

void CMainScene::UpdateLinePlayerCount(int32_t line_id, int32_t change_value)
{
	CLine* line = GetLine(line_id);
	if (NULL == line)
	{
		LOG_ERROR("default", "line is not exits : %d %d", scene_config_.scene_id(), line_id);
		return;
	}
	int32_t player_count = line->player_count();
	LOG_INFO("default", "player into game ,line[%d],player count[%d]", line->line_id(), line->player_count());
	line->set_player_count(player_count + change_value);
}

int32_t CMainScene::GenerateLineId() const
{
	int32_t line_id = 1;
	while (lines_.find(line_id) != lines_.end())
	{
		++line_id;
	}
	return line_id;
}
